use crate::driver_handle::DriverTrait;
use crate::tools::{drive_download, not_ascii_handler};
use crate::{
    Error as EtaxError, EtaxR, ReqContentType, ReqMethod, RequestOption, ResType, USER_AGENT,
};
use common_uu::arg::get_arg;
use common_uu::string::StringExentd;
use common_uu::{run_time, JsonV, JsonVExentd};
use pwner::process::Duplex;
use serde_json::Value;
use std::collections::HashMap;
use std::io::Write;
use std::ops::{Deref, DerefMut};
use std::option::Option::Some;
use std::path::Path;
use std::process::Command;
use std::time::Duration;
use thirtyfour::extensions::chrome::ChromeDevTools;
use thirtyfour::support::sleep;
use thirtyfour::By;
use thirtyfour::DesiredCapabilities;
pub use thirtyfour::Keys;
use thirtyfour::OptionRect;
use thirtyfour::{WebDriver, WebDriverCommands, WebElement};

/// 使用 thirtyfour 框架实现chrome_driver爬虫
pub async fn new<T: Sync + Send>(data: T) -> EtaxR<impl DriverTrait<T>> {
    let r = DriverImpl::new(data).await?;
    Ok(r)
}

pub struct DriverImpl<T: Sync + Send>
where
    Self: Sync + Send,
{
    driver: WebDriver,
    data: T,
    server_url: String,

    // 这个字段是为了在启动这个驱动之后,驱动跟随这个变量的销毁而关闭
    #[allow(dead_code)]
    cmd: Option<Duplex>,
}

/*
// #[cfg(not(test))]
impl<T: Sync + Send> Drop for DriverImpl<T> {
    fn drop(&mut self) {
        std::panic::set_hook(Box::new(|e| {
            error!("Driver drop panic: {:?}", e);
        }));
        let _ = async_std::task::block_on(async {
            let len = self
                .driver
                .window_handles()
                .await
                .map(|v| v.len())
                .unwrap_or(0);
            for i in 0..len {
                if let Err(e) = self.driver.close().await {
                    error!("driver.close({}): {:?}", i + 1, e);
                }
                sleep(Duration::from_secs(2)).await;
            }
            // Ok(())
        });
    }
}*/

#[test]
fn test_request() {
    use thirtyfour::support::*;
    std::env::set_var("driver.headless", "false");
    std::env::set_var("driver.port", "4445");
    std::env::set_var(
        "driver.remote_address",
        "http://127.0.0.1:4445,http://127.0.0.1:4444",
    );
    block_on(async {
        use DriverTrait;
        let driver = new(()).await.unwrap();
        driver
            .navigate_to("https://segmentfault.com/a/1190000015938472")
            .await
            .unwrap();
        let r = driver
            .request(
                "http://127.0.0.1:8080/etax/now/get".to_string(),
                crate::ReqMethod::post,
                json!({
                  "page_size": 10,
                  "data_types": "个税_登录,个税_申报,个税_缴款"
                }),
                Some(crate::ReqContentType::json),
                Some(crate::ResType::json),
            )
            .await;
        println!("r: {:?}", r);
        Ok(())
    })
    .unwrap();
}

/// 设置窗口大小
/// 注意: 在切换tab也需要设置一次,要不然在headless无头模式下, 截图的像素会非常小
async fn set_windows_size(driver: &WebDriver) -> EtaxR<()> {
    let mut rect = driver
        .get_window_rect()
        .await
        .map_err(|e| format!("{:?}", e))?;
    rect.x = 0;
    rect.width = 1920;
    rect.height = 1080;
    let option_rect = OptionRect::from(rect);
    driver
        .set_window_rect(option_rect)
        .await
        .map_err(|e| e.to_string())?;
    driver.maximize_window().await?;
    sleep_range().await;
    Ok(())
}

fn get_remote_address() -> Option<Vec<String>> {
    let remote_address = get_arg("driver.remote_address");
    let remote_address_list = match remote_address {
        JsonV::String(v) if !v.trim().is_empty() => Some(v.split_arr(",")),
        JsonV::Array(v) if !v.is_empty() => {
            let mut arr = vec![];
            for x in v {
                x.as_string2().map(|v| arr.push(v));
            }
            Some(arr)
        }
        _other => None,
    };
    remote_address_list
}

mod drive_utils {
    use super::*;
    use once_cell::sync::Lazy;
    use pwner::process::Duplex;

    pub async fn new() -> EtaxR<(WebDriver, String, Option<Duplex>)> {
        let mut drive = EtaxR::Err(crate::Error::RpaError("WebDriver is empty".into()));
        let remote_address_len = remote_address::len().await?;
        debug!("remote_address len: {:?}", remote_address_len);
        match remote_address_len {
            None => {
                drive = new_driver2(None)
                    .await
                    .map_err(|e| crate::Error::RpaError(e.to_string()));
            }
            Some(len) => {
                for _ in 0..len {
                    let remote_url = remote_address::get().await?;
                    info!("exce remote_address: {}", remote_url);
                    let init_timeout = get_arg("driver.init_timeout").as_u64().unwrap_or_else(|| 5);
                    let r = tokio::time::timeout(
                        Duration::from_secs(init_timeout),
                        new_driver2(Some(remote_url.clone())),
                    )
                    .await;
                    // let r = new_driver2(Some(remote_url.clone())).await;
                    match r {
                        Ok(Ok(v)) => {
                            drive = Ok(v);
                            break;
                        }
                        Ok(Err(e)) => {
                            drive = Err(crate::Error::RpaError(format!(
                                "webdrive init error remote_address({}): {:?}",
                                remote_url, e
                            )));
                            warn!("检测到无效的访问webdrive({}): {:?}", remote_url, e);
                            // remote_address::设置为无效2(remote_url)
                            //     .map_err(|e| crate::Error::RpaError(e.to_string()))?;
                            continue;
                        }
                        Err(e) => {
                            drive = Err(crate::Error::RpaError(format!(
                                "webdrive init error remote_address({}): {:?}",
                                remote_url, e
                            )));
                            warn!("检测到无效的访问webdrive({}): {:?}", remote_url, e);
                            // remote_address::设置为无效2(remote_url)
                            //     .map_err(|e| crate::Error::RpaError(e.to_string()))?;
                            continue;
                        }
                    }
                }
            }
        }

        drive
    }

    async fn run_local_webdrive() -> EtaxR<(String, Duplex)> {
        // 下载驱动,和获得闲置端口号,这两个逻辑都需要同步执行,所以需要用到锁机制
        use tokio::sync::Mutex;
        static LOCK: Lazy<Mutex<isize>> = Lazy::new(|| Mutex::new(1));
        let _lock = LOCK.lock().await;

        let download_url = get_arg("driver.download_url")
            .as_string2()
            .unwrap_or(String::from(
                "https://oss.npmmirror.com/dist/chromedriver/96.0.4664.45/chromedriver_win32.zip",
            ));
        let chromedriver = get_arg("driver.name")
            .as_string2()
            .unwrap_or(String::from("./chromedriver_v96.0.4664.45.exe"));
        drive_download(&download_url, &chromedriver)?;
        // let default_port = 4444;
        // let port: u64 = match get_arg("driver.port") {
        //     JsonV::String(v) => v.parse().unwrap_or_else(|_e| default_port),
        //     JsonV::Number(v) if v.is_u64() => v.as_u64().unwrap_or_else(|| default_port),
        //     _ => default_port,
        // };
        let port = port_check::free_local_port_in_range(4444, 5000)
            .ok_or_else(|| "[4444-5000]没有可用端口号")?;
        info!("开始执行chromedriver: {}, 开启端口:{}", &chromedriver, port);
        use pwner::Spawner;
        let cmd = Command::new(&chromedriver)
            .arg(format!("--port={}", port))
            .spawn_owned()
            .map_err(|e| format!("open [{}] is faild, {:?}", &chromedriver, e))?;
        tokio::time::sleep(std::time::Duration::from_millis(1600)).await;
        Ok((format!("http://localhost:{}", port), cmd))
    }

    #[test]
    fn test_port() {
        let port = port_check::free_local_port_in_range(4444, 5000).unwrap();
        // Command::new("chromedriver_v92.0.4515.107.exe")
        //     .arg(format!("--port={}", port))
        //     .spawn()
        //     .map_err(|e| format!("open [{}] is faild, {:?}", port, e))
        //     .unwrap();
        use pwner::Spawner;
        let child1 = Command::new("chromedriver_v96.0.4664.45.exe")
            .arg(format!("--port={}", port))
            .spawn_owned()
            .expect("ls command failed to start");
        std::thread::sleep(std::time::Duration::from_secs(1));

        let port = port_check::free_local_port_in_range(4444, 5000).unwrap();
        let _child2 = Command::new("chromedriver_v96.0.4664.45.exe")
            .arg(format!("--port={}", port))
            .spawn_owned()
            .expect("ls command failed to start");

        std::thread::sleep(std::time::Duration::from_secs(5));
        drop(child1);
        std::thread::sleep(std::time::Duration::from_secs(10));
        println!("{:?}", port)
    }

    async fn new_driver2(
        remote_address: Option<String>,
    ) -> EtaxR<(WebDriver, String, Option<Duplex>)> {
        let now_time = std::time::Instant::now();

        let (address, cmd) = match remote_address {
            Some(v) => (v, None),
            None => {
                let (s, d) = run_local_webdrive().await?;
                (s, Some(d))
            }
        };

        let headless = match get_arg("driver.headless") {
            JsonV::Bool(b) => b,
            JsonV::String(v) if v.trim() == "true" => true,
            JsonV::String(v) if v.trim() == "false" => false,
            _ => true,
        };

        // window.navigator.webdriver
        let mut caps = DesiredCapabilities::chrome();
        caps.set_disable_web_security()?;
        caps.add_chrome_arg(format!("user-agent={}", USER_AGENT).as_str())?;
        if headless {
            caps.set_headless()?;
        }
        // 从 Chrome 88开始，它的 V8 引擎升级了，一些接口发生了改变。
        // Google 关闭了一扇门的时候，又给你打开了一扇窗。
        // 实际上我们在使用 Selenium 调用 Chrome 的时候，只需要增加一个配置参数
        // 就可以再次隐藏 window.navigator.webdriver 了。
        caps.add_chrome_arg("--disable-blink-features=AutomationControlled")?;
        let driver: WebDriver = WebDriver::new(address.as_str(), &caps).await?;

        let dev_tools = ChromeDevTools::new(driver.session());
        dev_tools
            .execute_cdp_with_params(
                "Page.addScriptToEvaluateOnNewDocument",
                json!({ "source": crate::CDP_JS }),
            )
            .await
            .map_err(|e| format!("执行[stealth.min.js]出错: {:?}", e))?;

        set_windows_size(&driver).await?;
        info!(
            "初始化webdriver引擎耗时:{}毫秒",
            now_time.elapsed().as_millis()
        );

        Ok((driver, address, cmd))
    }
}

mod remote_address {
    use super::*;
    use common_uu::IResult;
    use once_cell::sync::Lazy;
    use std::ops::{Deref, DerefMut};
    use thirtyfour::{WebDriver, WebDriverCommands};
    use tokio::sync::Mutex;

    static REMOTE_ADDRESS_LOOP: Lazy<Mutex<Vec<(String, i64)>>> = Lazy::new(|| Mutex::new(vec![]));

    pub async fn len() -> EtaxR<Option<usize>> {
        let remote_address_list = get_remote_address();
        match remote_address_list {
            Some(v) => set(v).await?,
            None => return Ok(None),
        }
        let lock = REMOTE_ADDRESS_LOOP.lock().await;
        let arr = lock
            .deref()
            .iter()
            .filter(|v| v.1 != -1)
            .collect::<Vec<&(String, i64)>>();
        let r = EtaxR::Err(
            EtaxError::RpaError(format!("remote_address 全部无效了: {:?}", lock.deref())).into(),
        );
        if arr.is_empty() {
            error!("{:?}", r);
            return r;
        }

        Ok(Some(arr.len()))
    }

    pub async fn 设置为无效(_drive: &WebDriver, _server_url: String) -> IResult {
        #[cfg(feature = "use_hunan_etax")]
        {
            let r = _drive
                .execute_script(
                    r##"
        var doc = document.querySelector(".code_num"); 
        return doc && doc.innerText == '425';"##,
                )
                .await?;
            let r = r.value().as_bool().unwrap_or_default();
            if r {
                error!("425: {}", _server_url);
                设置为无效2(_server_url).await?;
                Err("错误码425, ip访问被拒绝")?;
            }
        }
        Ok(())
    }

    pub async fn 设置为无效2(server_url: String) -> IResult {
        let mut lock = REMOTE_ADDRESS_LOOP.lock().await;
        let r = lock.iter_mut().find(|(k, _v)| k == &server_url);
        if let Some((_k, v)) = r {
            *v = -1;
        } else {
            Err(format!("无效的访问地址: {}", server_url))?;
        }
        println!("设置为无效2:　{:?}", lock.deref());
        Ok(())
    }

    pub async fn get() -> IResult<String> {
        let remote_address_list = get_remote_address();
        match remote_address_list {
            Some(v) => set(v).await?,
            None => Err("get_remote_address is empty")?,
        }
        let mut lock = REMOTE_ADDRESS_LOOP.lock().await;
        let mut min_count = i64::MAX;
        let mut index = -1;

        let mut r = IResult::Err(format!("remote_address 全部无效了: {:?}", lock.deref()).into());
        for i in 0..lock.deref_mut().len() {
            let (k, count) = &mut lock.deref_mut()[i];
            if count == &-1 {
                continue;
            }
            if *count < min_count {
                min_count = *count;
                index = i as i64;
                r = Ok(k.to_string());
            }
        }

        if index != -1 {
            lock.deref_mut()
                .get_mut(index as usize)
                .map(|(_, v)| *v += 1);
        }

        if r.is_err() {
            error!("{:?}", r);
        }

        println!("remote_address list: {:?}", lock.deref());

        r
    }

    pub async fn set(arr: Vec<String>) -> IResult<()> {
        let mut is_yz = true;
        let mut lock = REMOTE_ADDRESS_LOOP.lock().await;
        if lock.deref().len() != arr.len() {
            is_yz = false;
        }
        if is_yz {
            for x in &arr {
                let dd = lock.deref();
                let mut is = false;
                for (k, _v) in dd {
                    if x == k {
                        is = true;
                    }
                }
                if !is {
                    is_yz = false;
                    break;
                }
            }
        }

        if !is_yz {
            let d = lock.deref_mut();
            d.clear();
            for x in arr {
                d.push((x, 0));
            }
        }

        Ok(())
    }
}

#[async_trait::async_trait]
impl<T: Sync + Send> DriverTrait<T> for DriverImpl<T> {
    async fn new(data: T) -> EtaxR<Self>
    where
        Self: Sized,
    {
        let driver = drive_utils::new().await?;
        return Ok(Self {
            data,
            driver: driver.0,
            server_url: driver.1,
            cmd: driver.2,
        });
    }

    async fn refresh(&self) -> EtaxR<()> {
        let r = self.driver.refresh().await?;
        crate::waiting_element!(self.driver, By::Tag("html"));
        Ok(r)
    }

    async fn navigate_to(&self, url: &str) -> EtaxR<()> {
        debug!("navigate_to: {}", url);
        // sleep(std::time::Duration::from_secs(2));

        // 如果当前导航页面等于目的URL,那么刷新页面
        {
            let cur_url = self
                .driver
                .current_url()
                .await
                .map(|v| v.as_str().contains(url))?;
            if cur_url {
                let r = self.driver.refresh().await?;
                crate::waiting_element!(self.driver, By::Tag("html"));
                return Ok(r);
            }
        }

        self.driver
            .get(url)
            .await
            .map_err(|e| EtaxError::Navigation {
                url: url.to_string(),
                error: format!("self.driver.get: {:?}", e),
            })?;
        crate::waiting_element!(self.driver, By::Tag("html"));
        remote_address::设置为无效(&self.driver, self.server_url.clone()).await?;
        Ok(())
    }

    async fn open_new_windows(&self, url: &str) -> EtaxR<()> {
        self.exec_js(&format!("window.open('{}')", url)).await?;
        sleep(Duration::from_millis(3000)).await;
        self.swith_tab_by_last().await?;
        crate::waiting_element!(self.driver, By::Tag("html"));
        remote_address::设置为无效(&self.driver, self.server_url.clone()).await?;
        Ok(())
    }

    async fn current_url(&self) -> EtaxR<String> {
        let r = self.driver.current_url().await?;
        Ok(r)
    }

    async fn send_keys(&self, selector: &str, keys: thirtyfour::TypingData) -> EtaxR<()> {
        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        self.driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await?
            .send_keys(keys)
            .await?;
        Ok(())
    }

    async fn click(&self, selector: &str) -> EtaxR<()> {
        debug!("action: click, selector: {}", selector);

        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        self.driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await?
            .click()
            .await?;
        sleep_range().await;
        Ok(())
    }

    async fn click_retry(&self, selector: &str, count: u64) -> EtaxR<()> {
        debug!("action: click, selector: {}", selector);

        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        crate::waiting_element!(
            self.driver,
            match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            },
            count
        )
        .click()
        .await?;
        sleep_range().await;
        Ok(())
    }

    async fn click_by_content(&self, selector: &str, content: &str) -> EtaxR<()> {
        debug!(
            "action: click_by_content, selector: {}, content: {}",
            selector, content
        );
        let elements = self
            .driver
            .find_elements(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;
        for element in &elements {
            let s = element
                .text()
                .await
                .map(|v| v.contains(content))
                .map_err(|e| EtaxError::GetValue(selector.to_string(), e.to_string()))?;
            if s {
                element
                    .click()
                    .await
                    .map_err(|e| EtaxError::ClickInvalid(selector.to_string(), e.to_string()))?;
                sleep_range().await;
                return Ok(());
            }
        }
        return Err(EtaxError::ElementNotFound(
            format!("selector:{},content: {}", selector, content),
            "".to_owned(),
        ));
    }

    async fn remove(&self, selector: &str) -> EtaxR<()> {
        let el = format!(r##"document.querySelector("{}").remove()"##, selector,);
        self.exec_js(&el).await.map(|_v| ())
    }

    async fn click_alert(&self) -> EtaxR<()> {
        let r = self
            .driver
            .switch_to()
            .alert()
            .accept()
            .await
            .map_err(|_e| EtaxError::ClickInvalid("alert 元素".to_owned(), "".to_owned()));
        debug!("click_alert: {:?}", &r);
        sleep_range().await;
        r
    }

    async fn blur(&self, _selector: Option<String>) -> EtaxR<()> {
        unimplemented!()
    }

    async fn is_exist(&self, selector: &str) -> EtaxR<bool> {
        let b = self.get_values(selector).await?.len() > 0;
        Ok(b)
    }

    async fn get_values(&self, selector: &str) -> EtaxR<Vec<String>> {
        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        let mut arr = vec![];
        let elements = self
            .driver
            .find_elements(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::ClickInvalid(selector.to_string(), e.to_string()))?;
        for element in elements {
            // continue display: none
            if element
                .get_css_property("display")
                .await
                .unwrap_or_default()
                .trim()
                == "none"
            {
                continue;
            }
            if let Ok(return_v) = get_vlaue2(selector, &element).await {
                arr.push(return_v);
            };
        }
        debug!("get_values selector:{} result:{:?}", selector, &arr);
        return Ok(arr);
    }

    async fn get_value(&self, selector: &str) -> EtaxR<String> {
        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        let element = self
            .driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await?;
        let return_v = get_vlaue2(selector, &element).await;
        debug!(
            "get_value selector: {}, return_value: {:?}",
            selector, &return_v
        );
        return_v
    }

    async fn get_value_retry(&self, selector: &str, count: u64) -> EtaxR<String> {
        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        let element = crate::waiting_element!(
            self.driver,
            match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            },
            count
        );
        let return_v = get_vlaue2(selector, &element).await;
        debug!(
            "get_value selector: {}, return_value: {:?}",
            selector, &return_v
        );
        return_v
    }

    async fn get_attribute(&self, selector: &str, attribute_name: &str) -> EtaxR<JsonV> {
        let element = crate::waiting_element!(
            self.driver,
            match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            },
            5
        );
        let result = element
            .get_attribute(attribute_name)
            .await
            .map(|v| match v {
                Some(v) => JsonV::String(v),
                None => JsonV::Null,
            })
            .map_err(|e| {
                warn!("{:?}", e);
                EtaxError::GetValue(
                    format!(
                        "get_attribute, selector {}, attribute_name:{}",
                        selector, attribute_name
                    ),
                    e.to_string(),
                )
            });
        debug!(
            "get_attribute selector: {}, return_value: {:?}",
            selector, &result
        );
        result
    }

    async fn get_attributes(&self, selector: &str, attribute_name: &str) -> EtaxR<Vec<String>> {
        let r = self
            .driver
            .find_elements(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await?;
        let mut arr = vec![];
        for x in r {
            if let Ok(Some(v)) = x.get_attribute(attribute_name).await {
                arr.push(v);
            };
        }
        Ok(arr)
    }

    async fn get_css_property(&self, selector: &str, css_name: &str) -> EtaxR<JsonV> {
        let element = crate::waiting_element!(
            self.driver,
            match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            },
            5
        );
        let result = element
            .get_css_property(css_name)
            .await
            .map(|v| JsonV::String(v))
            .map_err(|e| {
                warn!("{:?}", e);
                JsonV::Null
            });

        if let Ok(v) = result {
            Ok(v)
        } else {
            Ok(result.err().unwrap_or_default())
        }
    }

    async fn input(&self, selector: &str, value: JsonV) -> EtaxR<JsonV> {
        let element = crate::waiting_element!(
            self.driver,
            match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            },
            3
        );

        // 从输入值转换到String
        let s = match &value {
            JsonV::String(v) => format!("'{}'", v),
            JsonV::Number(v) => v.to_string(),
            _ => {
                return Err(EtaxError::InputError(
                    format!(
                        "输入值类型不支持; selector: {}, value: {:?}",
                        selector, value
                    ),
                    "".to_string(),
                ));
            }
        };

        debug!(
            "action: input, selector: {}, value: {}",
            selector,
            s.as_str()
        );

        let tab_name = element
            .tag_name()
            .await
            .map_err(|e| EtaxError::GetValue(selector.to_string(), e.to_string()))?;

        if tab_name == "input" || tab_name == "select" || tab_name == "textarea" {
            return self
                .exec_js_by_selector(
                    selector,
                    format!(
                        r##"return document.querySelector('{}').value={}"##,
                        selector.replace('\'', r##"""##),
                        s
                    )
                    .as_str(),
                )
                .await;
        }

        element
            .send_keys(s)
            .await
            .map_err(|e| EtaxError::InputError(selector.to_string(), e.to_string()))?;

        sleep_range().await;
        Ok(JsonV::Null)
    }

    async fn get_rec(&self, selector: &str) -> EtaxR<(i64, i64, i64, i64)> {
        let element = self
            .driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;
        let rec = element
            .rect()
            .await
            .map_err(|e| EtaxError::GetValue(selector.to_string(), e.to_string()))?;
        Ok((
            rec.x as i64,
            rec.y as i64,
            rec.width as i64,
            rec.height as i64,
        ))
    }

    async fn move_by_offset(&self, selector: &str, x: i32, y: i32) -> EtaxR<()> {
        let ref element = self
            .driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;

        // let mut rng = rand::thread_rng();
        let x2: i32 = x * 0.7 as i32;
        self.driver
            .action_chain()
            .click_and_hold_element(element)
            .move_by_offset(x2, y)
            .perform()
            .await?;

        let x = x - x2;

        self.driver
            .action_chain()
            .click_and_hold_element(element)
            .move_by_offset(x, y)
            .perform()
            .await?;

        // let mut rng = rand::thread_rng();
        // let move_x: i32 = rng.gen_range(-7, 0);
        let move_x = -7;

        self.driver
            .action_chain()
            .click_and_hold_element(element)
            .move_by_offset(move_x, y)
            .perform()
            .await?;
        self.driver
            .action_chain()
            .click_and_hold_element(element)
            .move_by_offset(move_x.abs(), y)
            .perform()
            .await?;

        self.driver
            .action_chain()
            .release()
            .perform()
            .await
            .map_err(|e| EtaxError::Drag(selector.to_string(), e.to_string()))
    }

    async fn move_element(
        &self,
        selector: &str,
        _position_from: (i64, i64),
        position_to: (i64, i64),
    ) -> EtaxR<()> {
        debug!(
            "拖动: selector: {:?}, position_to: {:?}",
            &selector, &position_to
        );
        let element = self
            .driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::Drag(selector.to_string(), e.to_string()))?;
        self.driver
            .action_chain()
            .click_and_hold_element(&element)
            .move_to(position_to.0 as i32, position_to.1 as i32)
            .release()
            .perform()
            .await
            .map_err(|e| EtaxError::Drag(selector.to_string(), e.to_string()))
    }

    async fn exec_js(&self, js: &str) -> EtaxR<JsonV> {
        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        let result = self
            .driver
            .execute_script(js)
            .await
            .map_err(|e| format!("执行exec_js({})报错: {:?}", js, e))?;
        Ok(result.value().clone())
    }

    async fn request_option(&self, url: String, op: RequestOption) -> EtaxR<JsonV> {
        let RequestOption {
            method,
            data,
            headers,
            req_content_type,
            res_type,
        } = op;
        self.req(url, method, data, req_content_type, res_type, headers)
            .await
    }

    async fn request(
        &self,
        url: String,
        method: crate::ReqMethod,
        data: JsonV,
        content_type: Option<crate::ReqContentType>,
        data_type: Option<crate::ResType>,
    ) -> EtaxR<JsonV> {
        self.req(
            url,
            method,
            data,
            content_type,
            data_type,
            Default::default(),
        )
        .await
    }

    async fn exec_js_by_selector(&self, selector: &str, js: &str) -> EtaxR<JsonV> {
        #[cfg(feature = "use_switch_last_tab")]
        swith_tab_by_last(&self.driver).await?;

        let _element = self
            .driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;
        let result = self
            .driver
            .execute_script(js)
            .await
            .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;
        Ok(result.value().clone())
    }

    async fn swith_tab(&self, index: i64) -> EtaxR<()> {
        let handles = self
            .driver
            .window_handles()
            .await
            .map_err(|e| EtaxError::TabNotFind(e.to_string()))?;
        let handle = handles
            .get(index as usize)
            .ok_or(EtaxError::TabNotFind("不存在此tab".to_owned()))?;
        let r = self
            .driver
            .switch_to()
            .window(handle)
            .await
            .map_err(|e| EtaxError::TabNotFind(e.to_string()));
        sleep_range().await;
        set_windows_size(&self.driver).await?;
        r
    }

    async fn swith_tab_by_last(&self) -> EtaxR<()> {
        let handles = self
            .driver
            .window_handles()
            .await
            .map_err(|e| format!("切换到最后一个tab报错: {:?}", e))?;
        let r = self.swith_tab((handles.len() - 1) as i64).await;
        sleep_range().await;
        r
    }

    async fn swith_iframe(&self, index: i64) -> EtaxR<()> {
        let r = self
            .driver
            .switch_to()
            .frame_number(index as u16)
            .await
            .map_err(|e| {
                EtaxError::ElementNotFound(format!("swith_iframe index:{}", index), e.to_string())
            });
        sleep_range().await;
        r
    }

    async fn close_tab(&self) -> EtaxR<()> {
        sleep_range().await;
        let len = self
            .driver
            .window_handles()
            .await
            .map_err(|e| format!("close_tab获得所有tab出错: {:?}", e))?
            .len();
        if len <= 1 {
            debug!("close_tab len is {}, cannot close tab", len);
            return Ok(());
        }
        let r = self
            .driver
            .close()
            .await
            .map_err(|e| EtaxError::RpaError(format!("close_tab 关闭tab出错: {:?}", e)));
        sleep_range().await;
        self.swith_tab_by_last().await?;
        r
    }

    async fn close_tab_with_all(&self) -> EtaxR<()> {
        let windows = self.driver.window_handles().await?;
        let len = windows.len();
        for i in (0..len).rev() {
            debug!("driver.close(): {}", i);
            let _ = self.driver.switch_to().window(&windows[i]).await;
            sleep(Duration::from_millis(2500)).await;
            if let Err(e) = self.driver.close().await {
                warn!("driver.close(): {:?}", e);
            }
        }
        info!("close_tab_with_all() is done!");
        Ok(())
    }

    async fn screenshot_as_base64(&self, selector: &str) -> EtaxR<String> {
        let element = self
            .driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;
        element
            .scroll_into_view()
            .await
            .map_err(|e| format!("截屏-滚动: {:?}", e))?;
        let png = element
            .screenshot_as_base64()
            .await
            .map_err(|e| EtaxError::RpaError(format!("截屏-元素序列化为图片: {:?}", e)))?;
        Ok(png)
    }

    async fn capture_screenshot(&self, selector: &str, path: &Path) -> EtaxR<()> {
        let element = self
            .driver
            .find_element(match selector {
                _ if selector.starts_with("//") => By::XPath(selector),
                _ => By::Css(selector),
            })
            .await
            .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;
        element
            .scroll_into_view()
            .await
            .map_err(|e| format!("截屏-滚动: {:?}", e))?;
        let png_vec = element
            .screenshot_as_png()
            .await
            .map_err(|e| EtaxError::RpaError(format!("截屏-元素序列化为图片: {:?}", e)))?;
        let mut file = std::fs::File::create(path).map_err(|e| {
            EtaxError::RpaError(format!("截屏-新建文件[{:?}]: [{:?}]", path.to_str(), e))
        })?;
        file.write_all(&png_vec).map_err(|e| {
            EtaxError::RpaError(format!("截屏-写入文件[{:?}]: {:?}", path.to_str(), e))
        })?;
        file.sync_all().map_err(|e| {
            EtaxError::RpaError(format!("截屏-同步文件[{:?}]: {:?}", path.to_str(), e))
        })
    }

    async fn add_cookie(&self, cookie: super::Cookie) -> EtaxR<()> {
        let conv_data = serde_json::to_value(&cookie)?;
        let r = serde_json::from_value::<thirtyfour::Cookie>(conv_data)?;
        self.driver.add_cookie(r).await?;
        Ok(())
    }

    /// 获得cookies键值对集合
    async fn get_cookies(&self) -> EtaxR<Vec<super::Cookie>> {
        let arr = self.driver.get_cookies().await?;
        let conv_data = serde_json::to_value(&arr)?;
        let r = serde_json::from_value::<Vec<super::Cookie>>(conv_data)?;
        Ok(r)
    }

    /// 获得cookies字符串
    async fn get_cookies2(&self) -> EtaxR<Option<String>> {
        let cookies = self.get_cookies().await?;
        let mut r_cookies = Option::<String>::None;
        for c in cookies.iter() {
            let v = match &c.value {
                JsonV::String(v) => v.to_string(),
                JsonV::Number(v) => v.to_string(),
                _ => Err("post_with_cookie 不支持的cookie类型")?,
            };
            let cookis_kv = format!("{}={}; ", c.name, v);
            match &mut r_cookies {
                Some(v) => v.push_str(&cookis_kv),
                None => r_cookies = Some(cookis_kv),
            }
        }
        Ok(r_cookies.map(|v| not_ascii_handler(v)))
    }

    /*async fn get_with_cookie<P: CookieParams + Send>(&self, params: P) -> EtaxR<JsonV> {
        let url = params.url();
        let mut headers = params.headers();
        if let Some(cookie) = self.get_cookies2().await? {
            headers.insert("Cookie".to_owned(), cookie);
        }
        let r = common_uu::my_request::get_with_header(url.to_string(), headers)?;
        Ok(r)
    }*/

    /*async fn post_with_cookie<P: CookieParams + Send>(
        &self,
        params: P,
        body: JsonV,
    ) -> EtaxR<JsonV> {
        let ref url = params.url();
        let mut headers = params.headers();

        if let Some(cookie) = self.get_cookies2().await? {
            headers.insert("Cookie".to_owned(), cookie);
        }
        let body = match &body {
            JsonV::String(v) => v.to_string(),
            JsonV::Null => "".to_string(),
            _ => body.to_string(),
        };
        let r = common_uu::my_request::post_with_header(url.to_string(), headers, body)?;
        Ok(r)
    }*/
}

impl<T: Sync + Send> DriverImpl<T> {
    async fn req(
        &self,
        url: String,
        method: ReqMethod,
        data: JsonV,
        content_type: Option<ReqContentType>,
        data_type: Option<ResType>,
        headers: HashMap<String, JsonV>,
    ) -> EtaxR<Value> {
        const JQ: &str = include_str!("jquery-3.6.0.min.js");
        let null = "null".to_string();
        let method = method.to_string();
        let content_type = content_type
            .map(|v| v.to_string())
            .unwrap_or_else(|| null.clone());
        let data_type = data_type
            .map(|v| v.to_string())
            .unwrap_or_else(|| null.clone());

        // xhrFields: {{
        //     withCredentials: true,
        // }},

        let ref js = format!(
            r##"
            
            if(typeof($) == 'undefined'){{
                {jquery};
            }}
            
            var done = arguments[0];
            var data = {data};
            var contentType = {contentType};
            var dataType = {dataType};
            if(contentType && data && contentType.indexOf('json') != -1) {{
                data = JSON.stringify(data);
            }}
            var headers = {headers};
                
            $.ajax({{
                url: {url},
                type: {method},
                async: true,
                contentType: contentType,
                dataType: dataType,
                headers: headers,
                timeout : 61 * 1000, //超时时间设置，单位毫秒
                success: done,
                data: data && data != 'null' ? data : null,
            }});
            
            "##,
            jquery = JQ,
            url = json!(url).to_string(),
            method = method,
            contentType = content_type,
            dataType = data_type,
            data = data.to_string(),
            headers = json!(headers).to_string(),
        );

        let now_time = tokio::time::Instant::now() + Duration::from_secs(35);
        let r = tokio::time::timeout_at(now_time, self.driver.execute_async_script(js)).await;
        let r = match r {
            Err(_timeout) => Err(crate::Error::Other(format!("Timeout: {}", url.clone())))?,
            Ok(v) => v,
        };

        let result = r.map_err(|e| format!("request: {}\nerror: {:?}", url, e))?;
        let r = result.value().clone();
        Ok(r)
    }
}

impl<T: Sync + Send> Deref for DriverImpl<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T: Sync + Send> DerefMut for DriverImpl<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.data
    }
}

impl<T: Sync + Send> Drop for DriverImpl<T> {
    fn drop(&mut self) {
        run_time::call_async_by_tokio(async {
            if let Err(e) = self.close_tab_with_all().await {
                error!("Drop close_tab_with_all: {:?}", e);
            }
        });
    }
}

/*fn move_fun() {
    drive
        .action_chain()
        .click_and_hold_element(element)
        .move_by_offset(x, y)
        .perform()
        .await?;
    let mut rng = rand::thread_rng();
    let sleep_time = rng.gen_range(100, 300);
    sleep(Duration::from_millis(sleep_time));
}*/

async fn sleep_range() {
    // let mut rng = rand::thread_rng();
    // let sleep_time = rng.gen_range(1000, 2000);
    sleep(Duration::from_millis(1500)).await;
}

/// 格式化错误信息
#[allow(dead_code)]
async fn err_string(selector: &str, e: impl std::fmt::Debug) -> String {
    format!("selector:{}, error:{:?}", selector, e)
}

async fn swith_tab_by_last(driver: &WebDriver) -> EtaxR<()> {
    let handles = driver
        .window_handles()
        .await
        .map_err(|e| EtaxError::TabNotFind(e.to_string()))?;
    let handle = handles
        .last()
        .ok_or(EtaxError::TabNotFind("不存在此tab".to_owned()))?;
    let r = driver
        .switch_to()
        .window(handle)
        .await
        .map_err(|e| EtaxError::TabNotFind(e.to_string()));
    r
}

async fn get_vlaue2(selector: &str, element: &WebElement<'_>) -> EtaxR<String> {
    let tag_name = element
        .tag_name()
        .await
        .map_err(|e| EtaxError::ElementNotFound(selector.to_string(), e.to_string()))?;
    if tag_name == "input" {
        let v = element
            .value()
            .await
            .map_err(|e| EtaxError::InputReturnValue(selector.to_string(), e.to_string()));
        match v {
            Ok(None) => Err(EtaxError::GetValue(
                format!("[{}]值为空", selector),
                "".to_owned(),
            ))?,
            _ => v.map(|v| v.unwrap_or_default()),
        }
    } else {
        element
            .text()
            .await
            .map_err(|e| EtaxError::GetValue(selector.to_string(), e.to_string()))
    }
}
